﻿/* UTF-8 */

/*
 * glyph.c: 此文件负责字符图形的生成，后端是Windows Typography或FreeType
 * Copyright 2016 tianxianglong. All rights reserved.
 */

/*
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:

 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef UNICODE
#define UNICODE
#endif

#ifndef _UNICODE
#define _UNICODE
#endif

#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif

#include <Windows.h>
#include <windowsx.h>
#include <string.h>

#include "glyph.h"

#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_BITMAP_H
#include FT_SFNT_NAMES_H
#include FT_TRUETYPE_IDS_H


struct fft
{
	FT_Library ft;
	FT_Face face;
	UINT fmt;
};

typedef struct fft fft_t, *fft_ptr;


int mkfont_ft(
	FT_Library ft, 
	fft_ptr font_ggo, 
	const char * font_path, 
	UINT pt, 
	UINT fmt
	)
{
	FT_Face hFace = 0;
	FT_Error err = 0;

	font_ggo->ft = ft;

	err = FT_New_Face(font_ggo->ft, font_path, 0, &font_ggo->face);
	if (0 != err)
	{
		MessageBoxW(NULL, L"无法加载字体文件。", L"错误", MB_OK | MB_ICONWARNING);
		return 0;
	}

	FT_Select_Charmap(font_ggo->face, FT_ENCODING_UNICODE);
	if (font_ggo->face->charmap == NULL)
	{
		FT_Done_Face(font_ggo->face);
		MessageBoxW(
			NULL,
			TEXT("字体不支持Unicode编码。"),
			TEXT("错误"),
			MB_OK | MB_ICONWARNING
			);
		return 0;
	}

	FT_Set_Char_Size(
		font_ggo->face,
		0,
		pt, /*in 26.6 fractional points*/
		96,
		96
		);

	font_ggo->fmt = fmt;
	return 0;
}

static void 
drbmp(HDC outdc, int x, int y, FT_GlyphSlot glyph)
{
	int pitch = glyph->bitmap.pitch;
	unsigned char *scanline = glyph->bitmap.buffer;
	int bpp = 0;
	switch (glyph->bitmap.pixel_mode)
	{
	case FT_PIXEL_MODE_MONO:
		bpp = 1;
		break;
	case FT_PIXEL_MODE_GRAY:
		bpp = 8;
		break;
	}

	for (UINT row = 0; row < glyph->bitmap.rows; ++row, scanline += pitch)
	{
		for (UINT i = 0; i < glyph->bitmap.width; ++i)
		{
			COLORREF color = pxcolor(scanline, i, bpp, glyph->bitmap.num_grays);
			
			SetPixel(
				outdc, 
				x + i + glyph->bitmap_left,
				y + row - glyph->bitmap_top,
				color
				);
		}
	}
}

void drtext_ft(
	HDC outdc,
struct fft *font_ggo,
	int x,
	int y,
	wchar_t *text,
	int c
	)
{
	for (size_t i = 0; i < c; i++)
	{
		FT_ULong ch = text[i];
		FT_UInt glyph_idx = FT_Get_Char_Index(font_ggo->face, ch);

		FT_Error error;
		error = FT_Load_Glyph(
			font_ggo->face,
			glyph_idx,
			FT_LOAD_DEFAULT
			);

		if (error != 0)
			continue;

		error = FT_Render_Glyph(font_ggo->face->glyph, font_ggo->fmt);
		if (error != 0)
			continue;

		drbmp(outdc, x, y, font_ggo->face->glyph);

		x += font_ggo->face->glyph->advance.x / 64;
	}
}

int
glyphprocess_ft(
	glyph_object *glyph_array,
	fft_ptr font,
	wchar_t *text,
	int cch
	)
{
	FT_Int32 flags = FT_LOAD_DEFAULT;
	switch (font->fmt)
	{
	case FT_RENDER_MODE_NORMAL:
		flags |= FT_LOAD_RENDER;
		break;
	case FT_RENDER_MODE_MONO:
	default:
		flags |= FT_LOAD_RENDER | FT_LOAD_MONOCHROME;
		break;
	}

	for (size_t i = 0; i < cch; i++)
	{
		FT_ULong ch = text[i];
		FT_UInt glyph_idx = FT_Get_Char_Index(font->face, ch);
		
		FT_Error error;
		error = FT_Load_Glyph(
			font->face,
			glyph_idx,
			flags
			);

		if (error == 0)
		{
			FT_GlyphSlot glyph = font->face->glyph;
			glyph->bitmap.pixel_mode;
			glyph->bitmap.buffer;

			size_t size = glyph->bitmap.rows * glyph->bitmap.pitch;

			glyph_array[i].buffer = size > 0 ? malloc(size) : NULL;
			memcpy(glyph_array[i].buffer, glyph->bitmap.buffer, size);
			
			glyph_array[i].cp = (wchar_t)ch;
			glyph_array[i].size.cy = glyph->bitmap.rows;
			glyph_array[i].size.cx = glyph->bitmap.width;
			glyph_array[i].pos.x = 0;
			glyph_array[i].pos.y = 0;
			glyph_array[i].orign.x = glyph->bitmap_left;
			glyph_array[i].orign.y = glyph->bitmap_top;
			glyph_array[i].advance = glyph->advance.x >> 6;
		}
		else
		{
			glyph_array[i].buffer = NULL;
			glyph_array[i].cp = (wchar_t)ch;
			glyph_array[i].size.cy = 0;
			glyph_array[i].size.cx = 0;
			glyph_array[i].pos.x = 0;
			glyph_array[i].pos.y = 0;
			glyph_array[i].orign.x = 0;
			glyph_array[i].orign.y = 0;
			glyph_array[i].advance = 0;
		}
	}

	return cch;
}
